package com.sharp.portal.controller;

import com.github.pagehelper.PageInfo;
import com.sharp.kernel.config.response.Response;
import com.sharp.kernel.config.response.ResponseHandler;
import com.sharp.kernel.constant.err.ErrCodeSys;
import com.sharp.kernel.mybatis.repository.BaseMysqlRepository;
import com.sharp.kernel.mybatis.repository.BaseOuterJoinRepository;
import com.sharp.kernel.utils.DbUtil;
import com.sharp.kernel.utils.ReflectionUtil;
import com.sharp.kernel.validate.Validator;
import com.sharp.kernel.vo.common.CommonResVO;
import com.sharp.kernel.vo.query.QueryRespVO;
import com.sharp.portal.utils.PortalUtil;
import com.sharp.portal.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.RowBounds;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import springfox.documentation.annotations.ApiIgnore;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.Sqls;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Title: BaseBindRelationController
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2021/5/22 22:15
 */
@Slf4j
@SuppressWarnings("unchecked,rawtypes")
public abstract class BaseBindRelationController<VO> extends PortalCommonController {

    private static final int VO_INDEX = 0;
    private final Class<VO> resultClazz = (Class<VO>) ReflectionUtil.getSuperClassGenericType(this.getClass(),
            VO_INDEX);

    @ApiIgnore
    @RequestMapping(value = "/model", method = RequestMethod.GET)
    public ResponseEntity<Response<ModelDisplayVO>> getModelDisplay(HttpServletRequest request) {
        String uri = request.getRequestURI();
        uri = uri.substring(0, uri.lastIndexOf("/"));
        ModelDisplayVO vo = PortalUtil.getModelDisplay(resultClazz);
        vo.setId(uri);
        return ResponseHandler.generateResponse(vo);
    }

    @ApiIgnore
    @RequestMapping(value = "/relation/model", method = RequestMethod.GET)
    public ResponseEntity<Response<RelationModelVO>> getRelationModel() {
        RelationModelVO vo = PortalUtil.getRelationModel(resultClazz);
        return ResponseHandler.generateResponse(vo);
    }

    @ApiIgnore
    @RequestMapping(value = "/layout", method = RequestMethod.GET)
    public ResponseEntity<Response<LinkedHashMap<String, FieldDisplayVO>>> getFieldDisplay() {
        LinkedHashMap<String, FieldDisplayVO> map = PortalUtil.getFieldDisplayMap(resultClazz);
        return ResponseHandler.generateResponse(map);
    }

    @ApiIgnore
    @RequestMapping(value = "/query", method = RequestMethod.POST)
    public ResponseEntity<Response<QueryRespVO<VO>>> query(HttpServletRequest request,
                                                           @RequestBody QueryConditionReq req) {
        RelationModelVO vo = PortalUtil.getRelationModel(resultClazz);
        PageInfo<?> pageInfo = DbUtil.buildPageInfo(req);
        if (CollectionUtils.isNotEmpty(req.getConditionList())) {
            req.getConditionList().addAll(fillCondition(request));
        } else {
            req.setConditionList(fillCondition(request));
        }
        Example example = PortalUtil.buildExampleByQueryConditionReq(req, repository(), resultClazz);
        example.selectProperties(PortalUtil.buildExampleSelectProperties(resultClazz));
        if (StringUtils.isNotBlank(vo.getOrderFieldName())) {
            example.orderBy(vo.getOrderFieldName()).asc();
        }
        PageInfo data = repository().select(example, pageInfo);
        QueryRespVO<VO> res = new QueryRespVO<>(data, resultClazz, true);
        return ResponseHandler.generateResponse(res);
    }

    protected List<ConditionVO> fillCondition(HttpServletRequest request) {
        return PortalUtil.fillCondition(request);
    }

    /**
     * 注入service
     *
     * @return 数据库接入服务
     */
    protected abstract BaseOuterJoinRepository repository();

    @ApiIgnore
    @RequestMapping(value = "/query/unbind", method = RequestMethod.POST)
    public ResponseEntity<Response<QueryRespVO<VO>>> queryUnbind(HttpServletRequest request,
                                                                 @RequestBody QueryConditionReq req) {
        String[] masterIdArray = request.getParameterMap().get("masterId");
        Validator.assertNotNull(masterIdArray, ErrCodeSys.PA_DATA_NOT_EXIST, "绑定主数据");
        if (CollectionUtils.isNotEmpty(req.getConditionList())) {
            req.getConditionList().addAll(fillCondition(request));
        } else {
            req.setConditionList(fillCondition(request));
        }
        Example example = PortalUtil.buildExampleByQueryConditionReq(req, repository(), resultClazz);
        Example.Criteria criteria = getUnbindCriteria(masterIdArray[0], example);
        example.and(criteria);
        RelationModelVO relationModelVO = PortalUtil.getRelationModel(resultClazz);
        example.excludeProperties(relationModelVO.getMasterIdFieldName());
        if (StringUtils.isNotBlank(relationModelVO.getOrderFieldName())) {
            example.excludeProperties(relationModelVO.getOrderFieldName());
        }
        example.setDistinct(true);
        PageInfo data = repository().select(example, req.getCurrentPage(), req.getPageSize());
        QueryRespVO<VO> res = new QueryRespVO<>(data, resultClazz, true);
        return ResponseHandler.generateResponse(res);
    }

    private Example.Criteria getUnbindCriteria(String value, Example example) {
        Example.Criteria criteria = example.createCriteria();
        RelationModelVO vo = PortalUtil.getRelationModel(resultClazz);
        criteria.orIsNull(vo.getMasterIdFieldName());
        criteria.orNotEqualTo(vo.getMasterIdFieldName(), value);
        Example alreadyBindExample = Example.builder(example.getEntityClass()).select(vo.getSlaveIdFieldName())
                .andWhere(Sqls.custom().andEqualTo(vo.getMasterIdFieldName(), value)).build();
        List<Object> alreadyBindIdList = ReflectionUtil.getFieldList(repository().select(alreadyBindExample),
                vo.getSlaveIdFieldName(), Object.class);
        if (CollectionUtils.isNotEmpty(alreadyBindIdList)) {
            example.and(example.createCriteria().andNotIn(vo.getSlaveIdFieldName(), alreadyBindIdList));
        }
        return criteria;
    }

    @ApiIgnore
    @RequestMapping(value = "/bind", method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Response<CommonResVO>> bind(@RequestBody List<Map<String, Object>> reqList) {
        int res = 0;
        if (CollectionUtils.isNotEmpty(reqList)) {
            for (Map<String, Object> entity : reqList) {
                beforeBind(entity);
                RelationModelVO vo = PortalUtil.getRelationModel(resultClazz);
                if (StringUtils.isNotBlank(vo.getOrderFieldName())) {
                    Integer count = 0;
                    try {
                        count = getBindLastOrder(entity, vo);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    entity.put(vo.getOrderFieldName(), count + 1);
                }
                res += bindRepository().insert(entity);
                afterBind(entity, res);
            }
        }
        return ResponseHandler.commonResponse(res);
    }

    protected void beforeBind(Map<String, Object> req) {
    }

    protected Integer getBindLastOrder(Map<String, Object> req, RelationModelVO vo) {
        Map<String, Object> map = new HashMap<>(1);
        map.put(vo.getMasterIdFieldName(), req.get(vo.getMasterIdFieldName()));
        Example example = bindRepository().getExample(map);
        example.orderBy(vo.getOrderFieldName()).desc();
        RowBounds rowBounds = new RowBounds(0, 1);
        List<Object> list = bindRepository().select(example, rowBounds);
        if (CollectionUtils.isNotEmpty(list)) {
            return ReflectionUtil.getValue(list.get(0), vo.getOrderFieldName(), Integer.class);
        } else {
            return 0;
        }

    }

    /**
     * 注入service
     *
     * @return 数据库接入服务
     */
    protected abstract BaseMysqlRepository bindRepository();

    protected void afterBind(Map<String, Object> req, int result) {
    }

    @ApiIgnore
    @RequestMapping(value = "/unbind", method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Response<CommonResVO>> unbind(@RequestBody List<Map<String, Object>> reqList) {
        int res = 0;
        if (CollectionUtils.isNotEmpty(reqList)) {
            for (Map<String, Object> req : reqList) {
                beforeUnbind(req);
                Example example = bindRepository().getExample(req);
                res += bindRepository().delete(example);
                afterUnbind(req, res);
            }
        }
        return ResponseHandler.commonResponse(res);
    }

    protected void beforeUnbind(Map<String, Object> req) {
    }

    protected void afterUnbind(Map<String, Object> req, int result) {
    }

    @ApiIgnore
    @RequestMapping(value = "/bind/order", method = RequestMethod.POST)
    public ResponseEntity<Response<CommonResVO>> order(@RequestParam(value = "order") Integer order,
                                                       @RequestBody Map<String, Object> req) {
        Example example = bindRepository().getExample(req);
        RelationModelVO vo = PortalUtil.getRelationModel(resultClazz);
        Object entity = DbUtil.buildEntity(req, Object.class);
        ReflectionUtil.setValue(entity, vo.getOrderFieldName(), order);
        int res = bindRepository().update(entity, example);
        return ResponseHandler.commonResponse(res);
    }
}
